package dbext

import (
	"database/sql"
	"fmt"
	"gitee.com/tjloved/tool"
	"gitee.com/tjloved/tool/helper"
	"strings"
)

type TableInfoGetter struct {
	ConnectionName string
}

func NewTableInfoGetter(ConnectionName string) *TableInfoGetter {
	return &TableInfoGetter{ConnectionName}
}

func (gt TableInfoGetter) GetTables(TableNames []string) []Table {
	var Tables []Table
	dbConfigMap := tool.Config().GetStringMapString("mysql." + gt.ConnectionName)
	query := tool.Db(gt.ConnectionName).Table("information_schema.TABLES").Where("TABLE_SCHEMA", dbConfigMap["database"])
	if len(TableNames) > 0 && TableNames[0] != "" {
		query = query.Where("TABLE_NAME in ?", TableNames)
	}
	query.Find(&Tables)
	return Tables
}

func (gt TableInfoGetter) GetColumns(tableName string) []Column {
	var Columns []Column
	dbConfigMap := tool.Config().GetStringMapString("mysql." + gt.ConnectionName)
	tool.Db(gt.ConnectionName).Table("information_schema.COLUMNS").Where("TABLE_SCHEMA", dbConfigMap["database"]).Where("TABLE_NAME", tableName).Find(&Columns)
	return Columns
}

type Table struct {
	Name    string `gorm:"column:TABLE_NAME"`
	Comment string `gorm:"column:TABLE_COMMENT"`
}

type Column struct {
	Schema     string         `gorm:"column:TABLE_SCHEMA"`
	Table      string         `gorm:"column:TABLE_NAME"`
	Field      string         `gorm:"column:COLUMN_NAME"`
	Type       string         `gorm:"column:COLUMN_TYPE"`
	Null       string         `gorm:"column:IS_NULLABLE"`
	Key        string         `gorm:"column:COLUMN_KEY"`
	Default    sql.NullString `gorm:"column:COLUMN_DEFAULT"`
	Extra      string         `gorm:"column:EXTRA"`
	Privileges string         `gorm:"column:PRIVILEGES"`
	Comment    string         `gorm:"column:COLUMN_COMMENT"`
}

func (c *Column) ToStructField() string {
	return " " + c.GetName() + " " + c.GetType() + " `" + c.GetGormTag() + "` " + c.GetComment()
}

func (c *Column) ToModifyDDL() string {
	defaultStr := ""
	if c.Default.Valid {
		defaultStr = "default '" + c.Default.String + "'"
	}
	return fmt.Sprintf("Alter Table %s Modify %s %s %s %s comment '%s'", c.Table, c.Field, c.Type, c.GetDDLNullString(), defaultStr, c.Comment)
}

func (c *Column) ToAddDDL() string {
	return fmt.Sprintf("Alter Table %s Add %s %s %s comment '%s'", c.Table, c.Field, c.Type, c.GetDDLNullString(), c.Comment)
}

func (c *Column) ToDropDDL() string {
	return fmt.Sprintf("ALTER TABLE %s DROP COLUMN %s", c.Table, c.Field)
}

func (c *Column) GetDDLNullString() string {
	if c.Null == "Yes" {
		return "null"
	} else {
		return "not null"
	}
}

func (c *Column) GetDDLDefaultString() string {
	if c.Null == "Yes" {
		return ""
	} else {
		if c.Default.Valid {
			return "default " + c.Default.String
		} else {
			return ""
		}
	}
}

func (c *Column) GetName() string {
	return helper.StringToCamelCase(c.Field)
}

func (c *Column) GetComment() string {
	if len(c.Comment) > 0 {
		return strings.Replace(strings.Replace(c.Comment, "\r", "\\r", -1), "\n", "\\n", -1)
	}
	return ""
}

func (c *Column) GetType() string {
	typeArr := strings.Split(c.Type, "(")
	typeArr1 := strings.Split(c.Type, ")")

	switch typeArr[0] {
	case "int":
		if typeArr1[1] == " unsigned" {
			return "uint64"
		} else {
			return "int64"
		}
	case "integer":
		if typeArr1[1] == " unsigned" {
			return "uint64"
		} else {
			return "int64"
		}
	case "mediumint":
		if typeArr1[1] == " unsigned" {
			return "uint64"
		} else {
			return "int64"
		}
	case "bit":
		if typeArr1[1] == " unsigned" {
			return "uint64"
		} else {
			return "int64"
		}
	case "year":
		if typeArr1[1] == " unsigned" {
			return "uint64"
		} else {
			return "int64"
		}
	case "smallint":
		if typeArr1[1] == " unsigned" {
			return "uint16"
		} else {
			return "int16"
		}
	case "tinyint":
		if typeArr1[1] == " unsigned" {
			return "uint8"
		} else {
			return "int8"
		}
	case "bigint":
		if typeArr1[1] == " unsigned" {
			return "uint64"
		} else {
			return "int64"
		}
	case "decimal":
		return "decimal.Decimal"
	case "double":
		return "float32"
	case "float":
		return "float32"
	case "real":
		return "float32"
	case "numeric":
		return "float32"
	case "timestamp":
		return "time.Time"
	case "datetime":
		//return "jsontime.JsonTime"
		return "time.Time"
	case "time":
		return "time.Time"
	case "date":
		return "time.Time"
	default:
		return "string"
	}
}

func (c *Column) GetGormTag() string {
	fieldContext := `gorm:"column:` + c.Field

	if c.Key == "PRI" {
		fieldContext = fieldContext + `;primaryKey`
	}
	if c.Key == "UNI" {
		fieldContext = fieldContext + `;unique`
	}
	if c.Extra == "auto_increment" {
		fieldContext = fieldContext + `;autoIncrement`
	}
	if c.Null == "NO" {
		fieldContext = fieldContext + `;not null`
	}
	return fieldContext + `"`
}

func (c *Column) IsTime() bool {
	if c.GetType() == "time.Time" {
		return true
	}
	return false
}
